(* Copyright 2007 dnAnalytics Project.
 *
 * Contributors to this file:
 * Jurgen Van Gael
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 * 
 * * Redistributions of source code must retain the above copyright notice, this 
 *   list of conditions and the following disclaimer.
 * * Redistributions in binary form must reproduce the above copyright notice, 
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 * * Neither the name of the dnAnalytics Project nor the names of its contributors
 *   may be used to endorse or promote products derived from this software without
 *   specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *)
#light

namespace dnAnalytics.LinearAlgebra

open dnAnalytics.LinearAlgebra

/// A module which implements functional dense vector operations.
module DenseMatrix =

    /// Initialize a matrix by calling a construction function for every element.
    let inline init (n: int) (m: int) f =
        let A = new DenseMatrix(n,m)
        for i=0 to n-1 do
            for j=0 to m-1 do
                A.[i,j] <- f i j
        A

    /// Create the identity matrix.
    let inline identity (n:int) =
        let A = new DenseMatrix(n, n, 0.0)
        for i=0 to n-1 do
            A.[i,i] <- 1.0
        A
    
    /// Create a matrix from a list of float lists. Every list in the master list specifies a row.
    let inline of_list (fll: float list list) =
        let n = List.length fll
        let m = List.length (List.hd fll)
        let A = DenseMatrix(n,m)
        fll |> List.iteri (fun i fl ->
                            if (List.length fl) <> m then failwith "Each subrow must be of the same length." else
                            List.iteri (fun j f -> A.[i,j] <- f) fl)
        A
    
    /// Create a matrix from a list of sequences. Every sequence in the master sequence specifies a row.
    let inline of_seq (fss: #seq<#seq<float>>) =
        let n = Seq.length fss
        let m = Seq.length (Seq.hd fss)
        let A = DenseMatrix(n,m)
        fss |> Seq.iteri (fun i fs ->
                            if (Seq.length fs) <> m then failwith "Each subrow must be of the same length." else
                            Seq.iteri (fun j f -> A.[i,j] <- f) fs)
        A

    /// Create a matrix from a 2D array of floating point numbers.
    let inline of_array2 (arr: float[,]) = new DenseMatrix(arr)

    /// Initialize a matrix by calling a construction function for every row.
    let inline init_row (n: int) (m: int) (f: int -> #Vector) =
        let A = new DenseMatrix(n,m)
        for i=0 to n-1 do
            let row = f i
            if row.Count <> m then failwith "Row generator does not create rows of the appropriate size."
            A.SetRow(i, row)
        A

    /// Initialize a matrix by calling a construction function for every column.
    let inline init_col (n: int) (m: int) (f: int -> #Vector) =
        let A = new DenseMatrix(n,m)
        for i=0 to m-1 do
            let col = f i
            if col.Count <> n then failwith "Column generator does not create columns of the appropriate size."
            A.SetColumn(i, col)
        A
    
    /// Create a matrix with the given entries.
    let inline init_dense (n: int) (m: int) (es: #seq<int * int * float>) =
        let A = new DenseMatrix(n,m)
        Seq.iter (fun (i,j,f) -> A.[i,j] <- f) es
        A
    
    /// Create a square matrix with constant diagonal entries.
    let inline constDiag (n: int) (f: float) =
        let A = new DenseMatrix(n,n)
        for i=0 to n-1 do
            A.[i,i] <- f
        A
    
    /// Create a square matrix with the vector elements on the diagonal.
    let inline diag (v: #Vector) =
        let n = v.Count
        let A = new DenseMatrix(n,n)
        for i=0 to n-1 do
            A.[i,i] <- v.Item(i)
        A
    
    /// Create a 1xn dimensional matrix from a row vector.
    let inline of_rowvector (v: #Vector) =
        let n = v.Count
        let A = new DenseMatrix(1, n)
        A.SetRow(0, v)
        A
    
    /// Create an nx1 dimensional matrix from a column vector.
    let inline of_vector (v: #Vector) =
        let n = v.Count
        let A = new DenseMatrix(n, 1)
        A.SetColumn(0, v)
        A